import Schame from "./Schame";
import DataSource from "./DataSource";
let uuid = require ('uuid');

/**
 * 数据库的操作类
 * hibernate中的session在开启事务的时候，调用的是getCurrentSession()保证这个session在同一个线程中使用  连接太多了 抛了一场
 *
 */
export default class Session {

    constructor() {

    }

    async build() {
        //一定要单例模式  否则连接数过多,抛出异常
        this.conn = await DataSource.getConnectionSync();
        console.log("连接池中获取连接...")
        return this;
    }


    /**
     * 开启事务
     * 返回error true 开启事务失败 undefined 开启事务成功
     */
    beginTransaction() {
        return new Promise((resolve, reject) => {
            this.conn.beginTransaction(err => {
                if (err) {
                    reject(err)
                } else {
                    resolve()
                }
            })
        })


    }

    /**
     *事务提交
     * 返回error true 开启事务失败 undefined 开启事务成功
     */
    commit() {
        if (this.conn)
            this.conn.commit(err => {
                if (err) {
                    throw(err)
                } else {
                    return Promise.resolve();
                }
            })
    }

    /**
     * 释放连接
     */
    release() {
        if (this.conn) {
            console.log("归还连接到连接池...")
            this.conn.release();
        }
    }

    /**
     * 事务回滚
     * 返回error true 开启事务失败 undefined 开启事务成功
     */
    rollback() {
        if (this.conn)
            this.conn.rollback(err => {
                if (err) {
                    return Promise.reject(err);
                } else {
                    return Promise.resolve();
                }
            })
    }

    /**
     * 执行Sql语句
     * @param sql
     * @param values
     */
    exeSQL(sql, values) {
        console.log(sql, values)
        return new Promise((resolve, reject) => {
            this.conn.query(sql, values, (err, result) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(result);
                }
            })
        })
    }

    /**
     * 查找表
     * @param table
     * @private
     */
    _findTable(table) {
        //查找是否存在这样的表，不存在抛出异常
        if (!Schame[table]) {
            throw ("no such a table named " + table);
        }
    }

    /**
     * 查找主键
     * @param table
     * @returns {undefined}
     * @private
     */
    _findPrimaryKey(table) {
        let pkField = undefined;
        for (let i in Schame[table]) {
            if (Schame[table][i] == 'pk' || Schame[table][i] == 'uuid') {
                pkField = i;
                break;
            }
        }
        if (!pkField) {
            throw ('no primary key!');
        }

        return pkField;
    }


    /**
     * 添加
     * @param table
     * @param entity
     * insert into table () values ()
     */
    save(table, entity) {
        this._findTable(table);
        const pkField = this._findPrimaryKey(table);
        let id = uuid.v1().toString().replace(/-/g, "");
        if (!entity) {
            throw ('entity is undefined!')
        }
        Reflect.set(entity, pkField, id);
        let values = [];
        let keysStr = "";
        let symbolStr = "";
        for (let attr in Schame[table]) {
            const type = Schame[table][attr];
            //设置创建时间(默认系统时间)
            if (type == 'ct' || type == 'ut') {
                entity[attr] = new Date();
            }

            if (entity[attr] && type != 'pk') {
                keysStr += attr;
                keysStr += ","
                values.push(entity[attr]);
                symbolStr += "?,"
            }
        }
        keysStr = keysStr.substring(0, keysStr.length - 1);
        symbolStr = symbolStr.substring(0, symbolStr.length - 1);
        const sql = "INSERT INTO " + table + " (" + keysStr + ") VALUES (" + symbolStr + ")";

        if (!pkField) {
            throw ('Schema ' + table + ' has no primary key!');
        }
        return new Promise((resolve, reject) => {
            this.exeSQL(sql, values).then(res => {
                Reflect.set(entity, pkField, res.insertId > 0 ? res.insertId : id);
                resolve(entity);
            }).catch(e => {
                reject(e);
            })
        })

    }

    /**
     * 更新
     * @param table
     * @param entity
     * @returns {Promise<void>}
     */
    update(table, entity) {
        this._findTable(table);
        if (!entity) {
            throw ('entity is undefined!')
        }
        let values = [];
        let keysStr = "";
        let pkField = undefined;
        for (let attr in Schame[table]) {
            const type = Schame[table][attr];
            if (type == 'pk' || type == 'uuid') {
                pkField = attr;
            } else if (type == 'ct') {
                continue;
            } else {
                //更新时间
                if (type == 'ut') {
                    entity[attr] = new Date();
                }
                const value = entity[attr];
                if (value || value == 0) {
                    values.push(value);
                    keysStr += attr;
                    keysStr += " = ?,";
                }
            }
        }
        if (!entity[pkField]) {
            throw ('entity has no primary key!');
        }
        values.push(entity[pkField]);
        keysStr = keysStr.substring(0, keysStr.length - 1);
        const sql = "UPDATE " + table + " SET " + keysStr + " WHERE " + pkField + " = ?";
        return this.exeSQL(sql, values);

    }

    /**
     * 添加并更新
     * @param table
     * @param entity
     */
    saveOrUpdate(table, entity) {
        this._findTable(table);
        if (!entity) {
            throw ({message: 'entity is undefined!'})
        }
        const pkField = this._findPrimaryKey(table);
        if (!pkField) {
            throw ({message: 'cannot delete this entity,because no primary key!'})
        }
        if (entity[pkField]) {
            return this.update(table, entity);
        } else {
            return this.save(table, entity);
        }

    }

    /**
     * 删除
     * @param table
     * @param entity
     */
    remove(table, entity) {
        this._findTable(table);
        if (!entity) {
            throw ({message: 'entity is undefined!'})
        }
        const pkField = this._findPrimaryKey(table);
        if (!entity[pkField]) {
            throw ({message: 'cannot delete this entity,because no primary key!'});
        }
        const sql = "DELETE FROM " + table + " WHERE " + pkField + " =  ? ";
        return this.exeSQL(sql, [entity[pkField]]);
    }

    /**
     * 查询 分页查询
     * @param sql
     * @param values
     */
    query(table, sql, values, start, size) {
        values = values ||[];
        this._findTable(table);
        const id = this._findPrimaryKey(table);
        let sqlStr = ""
        if (start != 'undefined' && size) {
            sqlStr = "SELECT * FROM " + table + " a INNER JOIN (SELECT " + id + " FROM " + table + " " + (sql ? sql : "") + " limit ?,?) b ON a." + id + " = b." + id;
            values.push(start);
            values.push(size);
        } else {
            sqlStr = "SELECT * FROM " + table + " " + (sql ? sql : "");
        }

        return this.exeSQL(sqlStr, values ? values : []);

    }

    /**
     * 执行Sql语句
     * @param sql
     * @param values
     * @param start
     * @param size
     * @returns {Promise<unknown>}
     */
    executeSQL(sql, values,start,size) {
        return this.exeSQL(sql, values);
    }

}


